package tablekey

import (
	"sync"

	"gitee.com/tomatomeatman/golang-repository/bricks3/function/data/integerutil"
	"gitee.com/tomatomeatman/golang-repository/bricks3/function/data/stringutil"
	"gitee.com/tomatomeatman/golang-repository/bricks3/model/globalvariable"
	"gitee.com/tomatomeatman/golang-repository/bricks3/utils/app/moduleutil"
	"gitee.com/tomatomeatman/golang-repository/bricks3/utils/sqlfactory"
)

var (
	syncTablekeyMap      = map[string]*sync.Mutex{} //序列锁集合
	syncCountTablekeyMap = map[string]int{}         //序列锁引用计数值集合,当值为0时同时清理'序列锁集合'
)

func init() {
	globalvariable.Add("Module@TableKeyDao", &TableKeyDao{})
}

/**
 * 记录编号序列管理表TableKey表基本业务操作结构体
 */
type TableKeyDao struct {
}

/**
 * 取各表(或序列)的新Id
 * @param formatLong 格式化长度(不足长度+0)
 * @param seriename 表名或序列名
 * @return
 */
func (dao TableKeyDao) GetNewId(formatLong int, seriename string) string {
	lock, ok := syncTablekeyMap[seriename]
	if !ok {
		syncTablekeyMap[seriename] = &sync.Mutex{}
		syncCountTablekeyMap[seriename] = 1 //计数值
		lock = syncTablekeyMap[seriename]
	} else {
		syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] + 1 //计数值
	}

	lock.Lock() //加锁

	iCount := dao.newValue(seriename)
	if iCount != 1 {
		iCount = dao.addTable(seriename)
		if iCount != 0 {
			syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] - 1 //计数值-1
			if syncCountTablekeyMap[seriename] < 1 {                              //计数值为0,剔除
				delete(syncCountTablekeyMap, seriename)
				delete(syncTablekeyMap, seriename)
			}

			lock.Unlock() //解锁
			return stringutil.SupplyZero(formatLong, 1)
		}

		syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] - 1 //计数值-1
		if syncCountTablekeyMap[seriename] < 1 {                              //计数值为0,剔除
			delete(syncCountTablekeyMap, seriename)
			delete(syncTablekeyMap, seriename)
		}

		lock.Unlock() //解锁
		return "0"    //说明是取值失败
	}

	id := dao.findLastId(seriename)
	if id == "0" {
		syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] - 1 //计数值-1
		if syncCountTablekeyMap[seriename] < 1 {                              //计数值为0,剔除
			delete(syncCountTablekeyMap, seriename)
			delete(syncTablekeyMap, seriename)
		}

		lock.Unlock() //解锁
		return "0"    //说明是取值失败
	}

	syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] - 1 //计数值-1
	if syncCountTablekeyMap[seriename] < 1 {                              //计数值为0,剔除
		delete(syncCountTablekeyMap, seriename)
		delete(syncTablekeyMap, seriename)
	}

	lock.Unlock() //解锁

	result := stringutil.SupplyZero(formatLong, id)

	return result
}

/**
 * 取各表的一批新Id
 * @param formatLong 格式化长度(不足长度+0)
 * @param seriename 表名或序列名
 * @param size 数量
 * @return
 */
func (dao TableKeyDao) GetNewIds(formatLong int, seriename string, size int) []string {
	lock, ok := syncTablekeyMap[seriename]
	if !ok {
		syncTablekeyMap[seriename] = &sync.Mutex{}
		syncCountTablekeyMap[seriename] = 1 //计数值
		lock = syncTablekeyMap[seriename]
	} else {
		syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] + 1 //计数值
	}

	lock.Lock() //加锁

	iCount := dao.newValues(seriename, size)
	if iCount != 1 {
		iCount = dao.addTableValues(seriename, size)
	}

	if iCount != 1 {
		syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] - 1 //计数值-1
		if syncCountTablekeyMap[seriename] < 1 {                              //计数值为0,剔除
			delete(syncCountTablekeyMap, seriename)
			delete(syncTablekeyMap, seriename)
		}

		lock.Unlock() //解锁
		return nil    //说明是取值失败
	}

	sLastId := dao.findLastId(seriename)

	syncCountTablekeyMap[seriename] = syncCountTablekeyMap[seriename] - 1 //计数值-1
	if syncCountTablekeyMap[seriename] < 1 {                              //计数值为0,剔除
		delete(syncCountTablekeyMap, seriename)
		delete(syncTablekeyMap, seriename)
	}

	lock.Unlock() //解锁

	iLastId := integerutil.ToInt(sLastId)

	result := []string{}
	for i := 0; i < size; i++ {
		vNewId := stringutil.SupplyZero(formatLong, iLastId+i+1)
		result = append(result, vNewId)
	}

	return result
}

/**
 * 重置
 * @param seriename 表名或序列名
 * @return
 */
func (dao TableKeyDao) Reset(seriename string) string {
	iCount := dao.reset(seriename)

	if iCount < 1 {
		return "0"
	}

	return "1"
}

/**
 * 更新到新sValue值
 * @param sType
 * @return
 */
func (dao TableKeyDao) newValue(seriename string) int {
	sql := "UPDATE TableKey SET sValue = (sValue +1), iVersion = (iVersion +1) WHERE sType = ?"

	bl, _, res := sqlfactory.ExecSql(moduleutil.GetMainDb(), sql, seriename)
	if !bl {
		return 0
	}

	return integerutil.ToInt(res, 0)
}

/**
 * 取最后的id值
 * @param sValue
 * @return
 */
func (dao TableKeyDao) findLastId(seriename string) string {
	sql := "SELECT sValue FROM TableKey WHERE sType = ?"

	sValue := "0"
	bl, _, _ := sqlfactory.Find(moduleutil.GetMainDb(), &sValue, sql, seriename)
	if !bl {
		return sValue
	}

	return sValue
}

/**
 * 添加表字段信息
 * @param seriename
 * @return
 */
func (dao TableKeyDao) addTable(seriename string) int {
	sql := `
	INSERT INTO TableKey (iId,sType,sValue,iIndex,iVersion)
	SELECT IFNULL(MAX(iId), 0) +1,?,'1',1,1
	FROM TableKey`

	bl, _, res := sqlfactory.ExecSql(moduleutil.GetMainDb(), sql, seriename)
	if !bl {
		return 0
	}

	return integerutil.ToInt(res, 0)
}

/**
 * 更新到新的一批sValue值
 * @param seriename
 * @param size
 * @return
 */
func (dao TableKeyDao) newValues(seriename string, size int) int {
	sql := `UPDATE TableKey SET sValue = (sValue +1 + ?), iVersion = (iVersion +1 + ?) WHERE sType = ?`
	bl, _, res := sqlfactory.ExecSql(moduleutil.GetMainDb(), sql, size, size, seriename)
	if !bl {
		return 0
	}

	return integerutil.ToInt(res, 0)
}

/**
 * 添加表字段信息,并设置初始值
 * @param seriename
 * @param size
 * @return
 */
func (dao TableKeyDao) addTableValues(seriename string, size int) int {
	sql := `INSERT INTO TableKey (iId, sType, sValue, iIndex, iVersion)
	SELECT IFNULL(MAX(iId), 0) +1, ?, ?, 1, ?
	FROM TableKey`

	bl, _, res := sqlfactory.ExecSql(moduleutil.GetMainDb(), sql, seriename, size, size)
	if !bl {
		return 0
	}

	return integerutil.ToInt(res, 0)
}

/**
 * 重置
 * @param seriename
 * @return
 */
func (dao TableKeyDao) reset(seriename string) int {
	sql := `UPDATE TableKey SET sValue = 1iVersion = (iVersion +1) WHERE sType = ?`

	bl, _, res := sqlfactory.ExecSql(moduleutil.GetMainDb(), sql, seriename)
	if !bl {
		return 0
	}

	return integerutil.ToInt(res, 0)
}
